#include "MysqlConnPool.h"

#define LOGGER_NAME "shadow.smysql.mysqlconnpool"

#include <iostream>

using std::cout;
using std::endl;

#define MYSQL_CONN_IDX_EXTRA -1

MysqlConnPool::MysqlConnPool() {
	_logger = log4cxx::Logger::getLogger(LOGGER_NAME);
}

MysqlConnPool::~MysqlConnPool() {}

MYSQL_CONN * MysqlConnPool::alloc_extra_conn() {
		MYSQL_CONN * mysql_conn = new MYSQL_CONN;
		mysql_conn->mysql = NULL;
		mysql_conn->index = MYSQL_CONN_IDX_EXTRA;
		int res = reconnect_mysql_resource(*mysql_conn);
		LOG4CXX_INFO(_logger, "try to alloc extra mysql conn, res:" << res);
		if (res < 0) {
				delete mysql_conn;
				return NULL;
		}
		return mysql_conn;
}

int MysqlConnPool::free_extra_conn(MYSQL_CONN * mysql_conn) {
		if (NULL == mysql_conn) {
				LOG4CXX_ERROR(_logger, "try to free extra mysql conn which is NULL");
				return -1;
		}
		if (MYSQL_CONN_IDX_EXTRA != mysql_conn->index) {
				LOG4CXX_ERROR(_logger, "try to free extra mysql conn which is not allocated by extra type");
				return -2;
		}
		mysql_close(mysql_conn->mysql);
		LOG4CXX_INFO(_logger, "try to free extra mysql conn");
		delete mysql_conn;
		return 1;
}

bool MysqlConnPool::is_extra_mysql_conn(MYSQL_CONN * mysql_conn) {
	   if (NULL == mysql_conn) {
		   LOG4CXX_ERROR(_logger, "is_extra_mysql_conn NULL parameter");
		   return false;
	   }
	   return MYSQL_CONN_IDX_EXTRA == mysql_conn->index;
}

int MysqlConnPool::init(int pool_num, const char *mysqld_host, int mysqld_port, const char * mysqld_db, \
				const char * mysqld_user, const char * mysqld_passwd) {
		if (pool_num < 1 || NULL == mysqld_host || mysqld_port < 1|| NULL == mysqld_user || NULL == mysqld_passwd) {
				LOG4CXX_ERROR(_logger, "mysql conn pool init error");
				return -1;
		}
		_mysqld_host = mysqld_host;
		_mysqld_port = mysqld_port;
		_mysqld_db = mysqld_db;
		_mysqld_user = mysqld_user;
		_mysqld_passwd = mysqld_passwd;
		int res = _res_pool.init(pool_num, -1);
		if (res < 0) return -2;
		for (int i = 0; i < pool_num; i++) {
				MYSQL_CONN conn;
				conn.mysql = NULL;
				conn.index = i;
				_conns.push_back(conn);
		}
		LOG4CXX_INFO(_logger, "mysql conn pool, init ok, mysqld_host:" << _mysqld_host << ", mysqld_port:" << \
						_mysqld_port << ", mysqld_db" << _mysqld_db << ", mysqld_user:" << _mysqld_user << \
						", mysqld_passwd:" << _mysqld_passwd);
		return 1;
}


MYSQL_CONN * MysqlConnPool::acquire_conn() {
		int res;
		int index = _res_pool.acquire_resource();
		if (index < 0) {
				MYSQL_CONN * pc = alloc_extra_conn();
				return pc;
				/*
				LOG4CXX_ERROR(_logger, "can not acquire mysql connection from pool, all connection is in use, "
								"mysqld_host:" << \
								_mysqld_host << ", mysqld_port:" << _mysqld_port << ", mysqld_db:" << _mysqld_db << \
								", mysqld_user:" << _mysqld_user << ", mysqld_passwd:" << _mysqld_passwd);
								
				return NULL;
				*/
		}
		MYSQL_CONN * pc = &_conns[index];
		if (NULL == pc->mysql || (0 != mysql_ping(pc->mysql) != 0)) {
				res = reconnect_mysql_resource(*pc);
				if (res < 0) {
						_res_pool.release_resource(index);
						return NULL;
				}
		}
		return pc;
}

int MysqlConnPool::release_conn(MYSQL_CONN * conn) {
		if (NULL == conn) {
				LOG4CXX_ERROR(_logger, "release mysql connection to resoure pool error, NULL mysql conn");
				return -1;
		}
		if (is_extra_mysql_conn(conn)) {
				free_extra_conn(conn);
				return 1;
		}
		int index = conn->index;
		_res_pool.release_resource(index);
		return 2;
}

int MysqlConnPool::reconnect_mysql_resource(MYSQL_CONN & conn) {
		if (NULL != conn.mysql) mysql_close(conn.mysql);
		MYSQL * mysql = mysql_init(NULL);
		if (NULL == mysql) {
				LOG4CXX_ERROR(_logger, "mysql init handler error");
				return -1;
		}
		MYSQL * t = mysql_real_connect(mysql, _mysqld_host.c_str(), _mysqld_user.c_str(), _mysqld_passwd.c_str(), \
								_mysqld_db.c_str(), _mysqld_port, NULL, 0);
		if (NULL == t) {
				LOG4CXX_ERROR(_logger,  "connect to mysqld server exception, pool index:" << conn.index << \
								", mysqld_host:" << _mysqld_host << ", mysqld_port:" << _mysqld_port << \
								", mysqld_db:" << _mysqld_db << ", mysqld_user:" << _mysqld_user << \
								", mysqld_passwd:" << _mysqld_passwd << ", mysql error:" << mysql_error(mysql));
				mysql_close(mysql);
				return -2;
		}
		LOG4CXX_INFO(_logger,  "connect to mysqld server ok, pool index:" << conn.index << \
								", mysqld_host:" << _mysqld_host << ", mysqld_port:" << _mysqld_port << \
								", mysqld_db:" << _mysqld_db << ", mysqld_user:" << _mysqld_user << \
								", mysqld_passwd:" << _mysqld_passwd );
	
		conn.mysql = mysql;
		return 1;
}
